#include "Node.h"
#include "worker-monitor-topics.h"
#include <stdlib.h>

Node::Node(TTime workingTime)
: messageSubscriber(nmx,messageBuffer),
  workerAliveSubscriber(workerAlive,workerAliveBuffer)
{
	workerTimeOut = workingTime;
	nodeID = -1;

}

Node::~Node() {
}

void Node::init() {
	//Done to make the gateway resumer resume our gateway
	gatewayResumer.setOwner(&topicGateway);
	StartSendStatusInfo(1000);
}

void Node::flushBuffers() {
	WMMessage tempTopicBuffer1;
	long tempTopicBuffer2;

	//Read from buffers until they are empty
	while(messageBuffer.get(tempTopicBuffer1));
	while(workerAliveBuffer.get(tempTopicBuffer2));
}

bool Node::EGSEHandleMessages(WMMessage topicInput,bool isworker){
	WMMessage output = {MEANINGLESS,-1,-1};
	if(topicInput.messageType == PING && (topicInput.nodeNumber == nodeID || topicInput.nodeNumber == -1)){
		output.messageType = PONG;
		output.nodeNumber = nodeID;
		output.genData = nodeID;
//				PRINTF(SCREEN_MOVE_CURSOR_TO, 1, 1);
//				PRINTF("PING");
		nmx.publish(output);
	}
	if(topicInput.messageType == identifyWorker && topicInput.nodeNumber == -1){
		if(isworker){
			output.nodeNumber = nodeID;
			output.messageType = identifyWorker;
			output.genData = nodeID;
			nmx.publish(output);
		}
	}
	if(topicInput.messageType == SetWorker){
		if(topicInput.nodeNumber != nodeID){
			isworker = false;
			PRINTF(SCREEN_MOVE_CURSOR_TO, 1, 20);
			PRINTF("Paused");
			waitUntil(NOW()+200*MILLISECONDS);
		}else{
			isworker = true;
		}
	}
	if(topicInput.messageType == SetMode && (topicInput.nodeNumber == nodeID || topicInput.nodeNumber == -1)){
		if(topicInput.genData == 1){
			oMode = NORMAL;
		}
	}
	return isworker;
}


bool Node::wHandleMessages() {
	//Variables for reading messages from the buffer
	WMMessage topicInput = {MEANINGLESS, -1, -1};
	long workerIDInput = -1;
	bool isworker = true;

	//This loop will probably be extended in future versions to handle messages
	while(messageBuffer.get(topicInput))
	{
		isworker = EGSEHandleMessages(topicInput,isworker);
	}

	//Checks for other workers publishing
	if(isworker){
		while(workerAliveBuffer.get(workerIDInput)) {
			if(workerIDInput != nodeID) {
				//This line should only be reached if there is another worker publishing
				if(workerIDInput < nodeID) {
					return false;
				}
			}
		}
	}
	//When execution reaches this point there is no other worker publishing
	return isworker;
}

bool Node::mHandleMessages() {
	WMMessage topicInput = {MEANINGLESS,-1,-1};
	//Variable used for buffer input
	long workerIDInput = -1;

	//Will be set to true when we find a worker
	bool workerDetected = false;

	bool isworker = false;

	//This loop will probably be extended in future versions to handle messages
	while(messageBuffer.get(topicInput))
	{
		isworker = EGSEHandleMessages(topicInput,isworker);
	}

	//Look for worker topics
	if(!isworker){
		while(workerAliveBuffer.get(workerIDInput)) {

			workerDetected = true;
		}
	}

	//If a worker could be found continue as monitor
	//else become worker
	return isworker;
}

void Node::StartSendStatusInfo(unsigned long interval){
	sendStatusData = true;
	timePublishInterval = interval*MILLISECONDS;
}

void Node::StopSendStatusInfo(){
	sendStatusData = false;
}


void Node::publishStatusData(StatusData* data) {
	if(data != 0 && sendStatusData && NOW() > timeLastPublish + timePublishInterval) {
		satelliteStatus.publish(*data,true);
		timeLastPublish = NOW();
	}
}

void Node::run() {
	//Time to wait for the network connection to be setup
	//May be reduced
	waitUntil(NOW()+5000*MILLISECONDS);
	
	
	//Detects the nodes ID
	nodeID = getNodeNumber();
	PRINTF(SCREEN_CLEAR);

	//Used to count the times a worker could not be detected
	short failcounter = 0;

	//Used for reading from the workerAlive topic
	long workerTopicID;

	//true when this node is the worker, false when the monitor
	//Each node starts as a monitor.
	bool worker = false;

	//Endless loop. Components should never stop working
	while(true) {

		PRINTF(SCREEN_CLEAR);

		PRINTF(SCREEN_MOVE_CURSOR_TO, 1, 1);
		PRINTF("This nodes ID: %i\n", nodeID);

		PRINTF(SCREEN_MOVE_CURSOR_TO, 2, 1);
		PRINTF("Being monitor now!\n");
		//Monitoring loop
		while(!worker) {
			//If there was no topic from the worker within the specified time...



			if(workerAlive.waitAndGet(workerTopicID,workerTimeOut) == -1) {
				//... then increase failcounter
				++failcounter;
				//If we couldn't detect a worker multiple times we become a worker ourself
				if(failcounter > 4) worker = true;
			}else {
				//Worker topic found, resetting the counter
				failcounter = 0;
				worker = mHandleMessages();
			}
		}

		//During role changes we flush the buffers
		flushBuffers();

		PRINTF(SCREEN_CLEAR);

		PRINTF(SCREEN_MOVE_CURSOR_TO, 1, 1);
		PRINTF("This nodes ID: %i\n", nodeID);

		PRINTF(SCREEN_MOVE_CURSOR_TO, 2, 1);
		PRINTF("Being the worker now!\n");

		//Working loop
		while(worker)
		{

			//Handle all messages
			worker = wHandleMessages();
			if(!worker){
				break;
			}
			//Publish worker topic //"I'm alive"
			workerAlive.publish(nodeID);

			//Work and publish results
			publishStatusData(work());
		}

		//During role changes we flush the buffers
		flushBuffers();
	}
}


/**
 * Class used to trigger the TimeEvents
 */
class TimeEventPropagator : public Thread
{
public:
	TimeEventPropagator() : Thread("TimeEventPropagation"){}
	/**
	 * Triggers the TimeEvents every 5 Milliseconds
	 */
	void run(){
		TIME_LOOP(NOW(),5*MILLISECONDS) {
			TimeEvent::propagate(NOW());
		}
	}
};

static TimeEventPropagator TEP;

